package com.lyb.server.hb.utils;

import org.apache.commons.codec.binary.Base64;

import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSAHelper -对RSA 签名&验签/分段加密&分段解密的包装签名算法: "SHA1withRSA", 私钥进行签名; 公钥进行验签.
 * 加密算法: "RSA/ECB/PKCS1Padding", 公钥进行加密; 私钥进行解密.
 * <p>
 * [localPrivKey]是自己的私钥, 自己的公钥给通信对方. [peerPubKey]是对方的公钥, 对方的私钥在对方那边. 为了方便,
 * 这里假定双方的密钥长度一致, 签名和加密的规则也一致.
 * <p>
 * 以`Base64Str`结尾的参数表示内容是Base64编码的字符串, 其他情况都是raw字符串.
 *
 * @author WuShuXin
 * @Date 2023-02-21
 */
public class RSAHelper {


    public static final String SIGNATURE_ALGORITHM = "SHA1withRSA";//签名算法SHA1withRSA、 SHA256withRSA
    public static int KEYBIT = 1024;

    public static final String KEY_ALGORITHM = "RSA";//秘钥格式
    private PrivateKey localPrivKey;
    private PublicKey peerPubKey;


    public RSAHelper() {

    }

    /**
     * 初始化自己的私钥,对方的公钥以及密钥长度.
     *
     * @param localPrivKeyBase64Str Base64编码的私钥,PKCS#8编码. (去掉pem文件中的头尾标识)
     * @param peerPubKeyBase64Str   Base64编码的公钥. (去掉pem文件中的头尾标识)
     */
    public void initKey(String localPrivKeyBase64Str,
                        String peerPubKeyBase64Str) throws Exception {
        try {
            if (localPrivKeyBase64Str != null && !localPrivKeyBase64Str.isEmpty()) {
                localPrivKey = getPrivateKey(localPrivKeyBase64Str);
            }
            if (peerPubKeyBase64Str != null && !peerPubKeyBase64Str.isEmpty()) {
                peerPubKey = getPublicKey(peerPubKeyBase64Str);
            }
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
    }


    /**
     * 从字符串中加载公钥
     *
     * @param publicKeyStr 公钥数据字符串
     * @throws Exception 加载公钥时产生的异常
     */
    public RSAPublicKey getPublicKey(String publicKeyStr) throws Exception {
        try {
            //byte[] buffer = Base64.getDecoder().decode(publicKeyStr);
            byte[] buffer = Base64RSA.decode(publicKeyStr.toCharArray());
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            RSAPublicKey publicKey = (RSAPublicKey) keyFactory
                    .generatePublic(keySpec);
            return publicKey;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        } catch (Exception e) {
            throw new Exception(e);
        }
    }


    /**
     * 从字符串中加载私钥
     *
     * @param privateKeyStr 公钥数据字符串
     * @throws Exception 加载私钥时产生的异常
     */
    public RSAPrivateKey getPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64RSA.decode(privateKeyStr.toCharArray());
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory
                    .generatePrivate(keySpec);
            return privateKey;

        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }


    /**
     * RSA签名
     *
     * @param plainBytes    需要签名的信息
     * @param useBase64Code 是否base64
     * @param charset       字符集
     * @return byte[]
     * @throws Exception
     */
    public byte[] signRSA(byte[] plainBytes, boolean useBase64Code,
                          String charset) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(localPrivKey);
        signature.update(plainBytes);

        // 如果是Base64编码的话，需要对签名后的数组以Base64编码
        if (useBase64Code) {
            return new String(Base64RSA.encode(signature.sign()))
                    .getBytes(charset);
        } else {
            return signature.sign();
        }
    }

    /**
     * RSA验签
     *
     * @param plainBytes 需要验签的信息
     * @param signBytes  签名信息
     * @return boolean
     */
    public boolean verifyRSA(byte[] plainBytes, byte[] signBytes,
                             boolean useBase64Code, String charset) {
        boolean isValid = false;
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(peerPubKey);
            signature.update(plainBytes);

            // 如果是Base64编码的话，需要对验签的数组以Base64解码
            if (useBase64Code) {
                isValid = signature.verify(Base64RSA.decode(new String(
                        signBytes, charset).toCharArray()));
            } else {
                isValid = signature.verify(signBytes);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isValid;
    }


    /**
     * 生成公钥、私钥
     *
     * @return
     */
    public static void createKey() {
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen;
        try {
            keyPairGen = KeyPairGenerator.getInstance("RSA");
            // 初始化密钥对生成器，密钥大小为96-1024位
            keyPairGen.initialize(KEYBIT, new SecureRandom());
            // 生成一个密钥对，保存在keyPair中
            KeyPair keyPair = keyPairGen.generateKeyPair();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();   // 得到私钥
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();  // 得到公钥
            String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
            // 得到私钥字符串
            String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
            System.out.println("私钥:" + privateKeyString);
            System.out.println("公钥:" + publicKeyString);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) throws Exception {
        //生成公钥私钥
		createKey();

		String privateKey="MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIHFNAmW06BomdphsJvq9eLpIP47N4KpXEvuYIKJaCZ7iYooV/P5n+MuhAmoGQqIwjEk7sgoDHYnwWT5dnLSt4zKjRZgsjUATaWHw3SbyIU+eOEHPLIG91N9oylEeeC4yz3LQ+otUrL5k+KTy9Vt8rpYW1SAnXwaoSee9jocgvyTAgMBAAECgYB70O6fn5r32BXDuaXKflpxUEI8j7+dGX6IrD7FDdmjrgdDHM8eZ7RYDI/KruqFlIrEXRq/Owb4zWHGuAZ98/NQM8e/DL1NM1256SaX4+f3hjz42ijw31Jz6mcbPXq/Hd//5/PwkA56/wkqqtwwA8rQnpmotDAnZ+ydf2EYpOfr4QJBAL+/2+qpWBaCmryHZRA0YQtb2mYpTm65hbgIL+6aoaDdmVeZxPI9q40HjekWd1tGlcCAMjHUj22EVgc9Rm/RQwsCQQCtQNDh0iigJ25di5AHxqGhwbl1z5I7nNAmr+d1XyIrsJGHhXX0/O1dBd4SGpnnOzg21Zwjf8xsT+X018vU2KGZAkAQ10z9Yzo1X+svO8UIk1EiqkKYTnBxESJJc46iKjqf4lahdwPxMrFqhYtVOX/qKFzmt1uxPLYMLF9R63PQRlTZAkBm2gWnKdYITaBNVF7q3tZxasoo0FMlpJBFh0PG//UPLsEWTPqbt9NJ2APfh386MIPMub75rBK1cjE3yywsbXZxAkBMB6I+zBgvLvTPmBysWxdS7yffVwvI3NMd5D1R69nzbFBE2pfY0AKeKFpN3kBFtRfZI3F0InLWBtU/WCB6dIms";
		String publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCBxTQJltOgaJnaYbCb6vXi6SD+OzeCqVxL7mCCiWgme4mKKFfz+Z/jLoQJqBkKiMIxJO7IKAx2J8Fk+XZy0reMyo0WYLI1AE2lh8N0m8iFPnjhBzyyBvdTfaMpRHnguMs9y0PqLVKy+ZPik8vVbfK6WFtUgJ18GqEnnvY6HIL8kwIDAQAB";
		String sign = RSAUtil.sign("123456", privateKey);//签名
		boolean verify = RSAUtil.verifySign("123456", sign, publicKey);//验签
		System.out.println(verify);

    }
}
